package uk.ac.strath.cis.religionsearch.model.indexer;

import org.apache.commons.digester.Digester;
import org.apache.commons.digester.ExtendedBaseRules;
import org.xml.sax.SAXException;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.Query;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

public class Indexer {
	private static IndexWriter writer;
	private File[] files;
	private String outputPath;
	private ArrayList<Tstmt> tstmts;

	public void startIndexing() throws IOException {
		Document contactDocument;
		String bktlong;
		String bktitle;
		ArrayList<verseContainer> bookcoll;
		System.out.print("Indexing Started...");
		for (int x = 0; x < tstmts.size(); x++) {
			Tstmt t = tstmts.get(x);
			bktitle = t.getTitle();
			bookcoll = t.getBooks();

			for (int i = 0; i < bookcoll.size(); i++) {
				Book currentBook = (Book) bookcoll.get(i);
				ArrayList<Chapter> chapters = currentBook.getChapters();
				bktlong = currentBook.getBktlong();

				for (int j = 0; j < chapters.size(); j++) {
					String s = chapters.get(j).getChtitle();
					ArrayList<String> verses = chapters.get(j).getVerses();
					int q = verses.size();

					for (int k = 0; k < q; k++) {
						contactDocument = new Document();
						contactDocument.add(new Field("verse", verses.get(k),
								Field.Store.YES, Field.Index.ANALYZED));
						contactDocument.add(new Field("verseAndChapterNumbers",
								((j + 1) + ":" + (k + 1)), Field.Store.YES,
								Field.Index.ANALYZED));
						contactDocument.add(new Field("chtitle", s,
								Field.Store.YES, Field.Index.ANALYZED));
						contactDocument.add(new Field("bktlong", bktlong,
								Field.Store.YES, Field.Index.ANALYZED));
						contactDocument.add(new Field("title", bktitle,
								Field.Store.YES, Field.Index.ANALYZED));
						contactDocument.add(new Field("tags", "",
								Field.Store.YES, Field.Index.ANALYZED));
						writer.addDocument(contactDocument);
					}
				}
			}
		}
		System.out.println("Done!");
	}

	public void updateTags(Document d, String tags) {
		System.out.print("Adding Tags \"" + tags + "\"...");
		FSDirectory dir;
		try {
			dir = FSDirectory.open(new File(outputPath));

			Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_CURRENT);
			boolean createFlag = false;
			writer = new IndexWriter(dir, analyzer, createFlag,
					IndexWriter.MaxFieldLength.UNLIMITED);

			d.removeField("tags");

			d
					.add(new Field("tags", tags, Field.Store.YES,
							Field.Index.ANALYZED));
			Query q = new QueryParser(Version.LUCENE_CURRENT, "verse",
					new StandardAnalyzer(Version.LUCENE_CURRENT)).parse("\""
					+ d.get("verse") + "\"");
			writer.deleteDocuments(q);
			writer.addDocument(d, analyzer);
			writer.optimize();
			writer.close();

		} catch (Exception e) {
			e.printStackTrace();
		}

		System.out.println("Done!");
	}

	public void storeTstmtForIndexing(Tstmt t) {
		if (tstmts == null)
			tstmts = new ArrayList<Tstmt>();
		tstmts.add(t);
	}

	public void init(File[] children, String outputPath) throws IOException,
			SAXException {
		System.out.println("Indexer initialised...");
		this.files = children;
		this.outputPath = outputPath;
		start();

	}

	public void start() throws IOException, SAXException {

		FSDirectory dir = FSDirectory.open(new File(outputPath));
		Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_CURRENT);
		boolean createFlag = true;
		writer = new IndexWriter(dir, analyzer, createFlag,
				IndexWriter.MaxFieldLength.UNLIMITED);

		for (int i = 0; i < files.length; i++) { // THIS WILL OVERWRITE THE
			// INDEX FOR EVERY FILE -
			// FIX!

			System.out.println("Opening file " + files[i].getAbsolutePath());
			System.out.print("Parsing Started...");

			Digester digester = new Digester();
			digester.setValidating(false);
			digester.setRules(new ExtendedBaseRules());
			if (files[i].getName().equals("quran.xml")) {
				digestQuran(digester);
			} else if(files[i].getName().equals("bom.xml")){
				
				digester.addObjectCreate("tstmt/", Tstmt.class);
				digester.addBeanPropertySetter("tstmt/coverpg/title", "title");

				digester.addObjectCreate("tstmt/bookcoll/book", Book.class);
				digester.addCallMethod("tstmt/bookcoll/book/bktlong",
						"setBktlong", 0);

				digester.addObjectCreate("tstmt/bookcoll/book/chapter",
						Chapter.class);
				digester.addCallMethod("tstmt/bookcoll/book/chapter/chtitle",
						"setChtitle", 0);
				digester.addCallMethod("tstmt/bookcoll/book/chapter/v",
						"setVerse", 0);
				digester.addCallMethod("tstmt/bookcoll/book/chapter/div/v",
						"setVerse", 0);
				digester
						.addSetNext("tstmt/bookcoll/book/chapter", "addChapter");			
				digester.addSetNext("tstmt/bookcoll/book", "addBook");
				
				digester.addObjectCreate("tstmt/bookcoll/sura", Book.class);
				digester.addCallMethod("tstmt/bookcoll/sura/bktlong", "suraBuild", 0);
				digester.addCallMethod("tstmt/bookcoll/sura/v", "suraVerse", 0);
				digester.addSetNext("tstmt/bookcoll/sura", "addBook");
				
			}
			
			else {

				digester.addObjectCreate("tstmt/", Tstmt.class);
				digester.addBeanPropertySetter("tstmt/coverpg/title", "title");

				digester.addObjectCreate("tstmt/bookcoll/book", Book.class);
				digester.addCallMethod("tstmt/bookcoll/book/bktlong",
						"setBktlong", 0);

				digester.addObjectCreate("tstmt/bookcoll/book/chapter",
						Chapter.class);
				digester.addCallMethod("tstmt/bookcoll/book/chapter/chtitle",
						"setChtitle", 0);
				digester.addCallMethod("tstmt/bookcoll/book/chapter/v",
						"setVerse", 0);
				digester.addCallMethod("tstmt/bookcoll/book/chapter/div/v",
						"setVerse", 0);
				digester
						.addSetNext("tstmt/bookcoll/book/chapter", "addChapter");			
				digester.addSetNext("tstmt/bookcoll/book", "addBook");				
			}
			System.out.println("Done!");

			storeTstmtForIndexing((Tstmt) digester.parse(files[i]));

		}
		startIndexing();
		writer.optimize();
		writer.close();

	}

	private void digestQuran(Digester digester) {
		digester.addObjectCreate("tstmt/", Tstmt.class);
		digester.addBeanPropertySetter("tstmt/coverpg/title", "title");
		digester.addObjectCreate("tstmt/suracoll/sura", Book.class);
		digester.addCallMethod("tstmt/suracoll/sura/bktlong", "suraBuild", 0);
		digester.addCallMethod("tstmt/suracoll/sura/v", "suraVerse", 0);
		digester.addSetNext("tstmt/suracoll/sura", "addBook");

	}

	public static interface verseContainer {
		public boolean hasChapters();

	}

	public static class Tstmt {
		private String title; // E.g. The New Testament
		private ArrayList<verseContainer> bookcoll;

		public ArrayList<verseContainer> getBooks() {
			return bookcoll;
		}

		public void addBook(Book b) {
			if (bookcoll == null)
				bookcoll = new ArrayList<verseContainer>();
			bookcoll.add(b);
		}

		public String getTitle() {
			return title;
		}

		public void setTitle(String title) {
			this.title = title;
		}
	}

	public static class Book implements verseContainer {
		private String bktlong; // E.g. The gospel according to saint mark
		private ArrayList<Chapter> chapters;
		private Chapter sura;

		public ArrayList<Chapter> getChapters() {
			return chapters;
		}

		public void addChapter(Chapter c) {
			if (chapters == null)
				chapters = new ArrayList<Chapter>();
			chapters.add(c);
		}

		public String getBktlong() {
			return bktlong;
		}

		public void setBktlong(String bktlong) {
			this.bktlong = bktlong;
		}

		@Override
		public boolean hasChapters() {
			return true;
		}

		public void suraBuild(String bktlong) {
			this.bktlong = bktlong;
			sura = new Chapter();
			sura.setChtitle(bktlong);
			addChapter(sura);
		}

		public void suraVerse(String verse) {
			sura.setVerse(verse);
		}
	}

	public static class Chapter {

		private String chtitle; // E.g. chapter 5
		private ArrayList<String> verses;

		public ArrayList<String> getVerses() {
			return verses;
		}

		public void setVerse(String verse) {
			if (verses == null)
				verses = new ArrayList<String>();
			verses.add(verse);
		}

		public String getChtitle() {
			return chtitle;
		}

		public void setChtitle(String chtitle) {
			this.chtitle = chtitle;
		}
	}

}
